//
//  hook.hpp
//  diylane-api
//
//  Created by Yucheng on 8/8/17.
//  Copyright © 2017 Yucheng. All rights reserved.
//

#ifndef hook_h
#define hook_h

#include <unistd.h>
#include <sys/mman.h>
#include <dlfcn.h>

namespace diylane {
    
    struct hook_info {
        void* addr;
        unsigned char head[32];
        int offset;
    };
    
    struct bigint {
        unsigned int hi;
        unsigned int lo;
    };
    
    struct mem {
        
        template <typename Addr, typename MyFunc, typename Original>
        static bool HookAddr(Addr addr, MyFunc myFunc, Original original, int offset) {
            
            static char buffer[4096] = {0};
            static int iHook = 0;
            hook_info* hookList = (hook_info*)buffer;
            unsigned char* jumpTo = 0;
            unsigned char* jumpStart = 0;
            bigint* bi = 0;
            
            hook_info* hook = &hookList[iHook++];
            
            // save info
            hook->addr = (void*)addr;
            memcpy(hook->head, (void*)addr, 32);
            *(void**)original = (void*)&hook->head;
            hook->offset = offset;
            
            // hook - target
            jumpStart = (unsigned char*)hook->head + offset;
            jumpTo = (unsigned char*)addr + offset;
            bi = (bigint*)jumpTo;
            *jumpStart = 0x68;                      jumpStart += 1;
            *(unsigned int*)jumpStart = bi->lo;     jumpStart += 4;
            *jumpStart = 0x68;                      jumpStart += 1;
            *(unsigned int*)jumpStart = bi->hi;     jumpStart += 4;
            *jumpStart = 0xC3;
            if ( 0 != mprotect(hook->head, 4096, PROT_EXEC|PROT_READ)) {
                printf("errno: %d, %s\n", errno, strerror(errno));
                return false;
            }
            
            // hook - source
            if ( 0 != mprotect((void*)addr, 4096, PROT_EXEC|PROT_WRITE|PROT_READ)) {
                printf("errno: %d, %s\n", errno, strerror(errno));
                return false;
            }
            jumpStart = (unsigned char*)addr;
            jumpTo = (unsigned char*)myFunc;
            *jumpStart = 0x68;                      jumpStart += 1;
            *(unsigned int*)jumpStart = bi->lo;     jumpStart += 4;
            *jumpStart = 0x68;                      jumpStart += 1;
            *(unsigned int*)jumpStart = bi->hi;     jumpStart += 4;
            *jumpStart = 0xC3;
            
            return true;
        }
        
    };
}

#endif /* hook_h */
